/**
 * 断言 真
 * @param expect 期望值
 * @param messages 断言失败时输出的提示信息
 */
function assertTrue(expect: boolean, ...messages: any) {
    if (expect === true) { return; }
    if (arguments.length != 1) {
        console.log(messages);
    }
    throw new Error(`expect true but ${expect}`);
}

/**
 * 断言 执行方法
 * @param expect true: 断言成功; false: 断言失败;
 * @param fn 需要执行的方法
 * @param config 配置项
 */
function assert<T>(
    /** true: 断言成功; false: 断言失败; */
    expect: boolean,
    /** 需要执行的方法 */
    fn: Tfunction<T>,
    /** 配置项 */
    config?: {
        /**
         * 执行方法 成功时 回调方法
         * @param result 执行方法返回值
         */
        success?: (result: T) => void,

        /**
         * 执行方法 成功时 回调 用于校验结果 方法
         * @param result 执行方法返回值
         * @returns true: 校验成功; false: 校验失败;
         */
        check?: (result: T) => boolean,

        /**
         * 执行方法 失败时 回调方法
         * @param error 执行方法抛出的错误
         */
        error?: (error: any) => void,

        /** 输出标题 */
        title?: string,

        /** 成功时 是否 控制台输出 信息 */
        showWhenSuccess?: boolean,

        /** 失败时 是否 控制台输出 信息 */
        showWhenError?: boolean,

        /** 控制台输出 的 附加信息 */
        additionals?: Array<{ title: string, value: any }>,
    }
) {
    config = config || {};
    config.additionals = config.additionals || [];
    let result;
    try {
        result = fn();
    } catch (err) {
        const showWhenError = config.showWhenError !== false;
        if (showWhenError) {
            config.title && console.log(config.title);
            config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
            console.log('error => ', err);
        }

        config.error && safeRun(config.error, 'error', err);
        if (showWhenError) { console.log(''); }
        if (expect) {
            if (!showWhenError) {
                config.title && console.log(config.title);
                config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
                console.log('error => ', err);
                console.log('');
            }
            throw new Error('expect success but error');
        }
        return;
    }

    const showWhenSuccess = config.showWhenSuccess !== false;
    if (showWhenSuccess) {
        config.title && console.log(config.title);
        config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
        console.log('result => ', result);
    }

    if (config.check) {
        try {
            const checkResult = config.check(result);
            if (checkResult !== true) {
                config.title && console.log(config.title);
                config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
                console.log('result => ', result);
                throw new Error('check failed');
            }
        } catch (error) {
            config.title && console.log(config.title);
            config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
            console.log('result => ', result);
            console.log('error => ', error);
            throw new Error('check failed');
        }
    }

    config.success && safeRun(config.success, 'success', result);
    if (showWhenSuccess) { console.log(''); }
    if (!expect) {
        if (!showWhenSuccess) {
            config.title && console.log(config.title);
            config.additionals.forEach(additional => { console.log(additional.title, ' => ', additional.value); });
            console.log('result => ', result);
            console.log('');
        }
        throw new Error('expect error but success');
    }
}

/**
 * 执行方法 当有错误发生时 捕获该错误 输出到控制台
 * @param fn 方法
 * @param fnName 方法名
 * @param args 方法所需参数
 * @returns 方法执行结果
 */
function safeRun(fn: Function, fnName: string, ...args: Array<any>) {
    try {
        return fn(...args);
    } catch (error) {
        console.log(`exeute function [ ${fnName} ] failed.`);
        console.log(error);
    }
}

/**
 * 断言 集合中的键 与 数组内容匹配
 * @param map 集合
 * @param array 集合中的键 匹配的数组
 * @param order true: 按照顺序匹配; false: 只匹配内容;
 */
function assertMatcheMapKeys<T>(map: Map<T, any>, array: Array<T>, order: boolean = false) {
    if (map.size !== array.length) { throw new Error(`map.size[${map.size}] !== array.length[${array.length}]\n\t\tmap keys: [ ${[...map.keys()].join(', ')} ]\n\t\tarray: [ ${array.join(', ')} ]`); }
    array = [...array];
    if (order === true) {
        let i = 0;
        for (const [key, value] of map) {
            const item = array[i];
            if (key !== item) { throw new Error(`key[${key}] not equal to [${item}] in array [${i}]`); }
            i++;
        }
        return;
    } else {
        for (const [key, value] of map) {
            const i = array.indexOf(key);
            if (i < 0) { throw new Error(`key[${key}] not in array [ ${array.join(', ')} ]`); }
            array.splice(i, 1);
        }
    }
    if (array.length != 0) { throw new Error(`there are still values left in the array [ ${array.join(', ')} ]`); }
}
